package com.rtf.framework.monitor.export;

import com.rtf.framework.monitor.AppMonitorExporter;
import com.rtf.framework.monitor.config.AppMonitorProperties;
import com.rtf.framework.monitor.support.AppMonitorNameConverter;
import com.google.common.collect.Lists;
import com.netflix.hystrix.HystrixCircuitBreaker;
import com.netflix.hystrix.HystrixCommandMetrics;
import com.netflix.hystrix.HystrixEventType;
import com.netflix.hystrix.HystrixThreadPoolMetrics;
import com.netflix.hystrix.util.HystrixRollingNumberEvent;
import io.prometheus.client.Collector;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.GaugeMetricFamily;
import lombok.Setter;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * hystrix监控导出
 * @Author : liupeng
 * @Date : 2020-01-28
 * @Modified By
 */
public class AppHystrixExport extends Collector implements AppMonitorExporter {

    @Setter
    private static AppMonitorNameConverter commandNameConverter = new AppMonitorNameConverter.Default() ;

    @Setter
    private static AppMonitorNameConverter groupNameConverter = new AppMonitorNameConverter.Default() ;

    // hystrix默认滚动窗口的大小
    public static final int defaultMetricsRollingStatisticalWindow = 10 ;

    @Override
    public String getName() {
        return "hystrix";
    }

    @Override
    public synchronized void export(CollectorRegistry collectorRegistry) {
        new AppHystrixExport().register( collectorRegistry ) ;
    }

    @Override
    public List<MetricFamilySamples> collect() {
        List<MetricFamilySamples> mfs = Lists.newArrayList( ) ;

        collectHystrixThreadPoolMetrics( mfs ) ;

        collectHystrixCommandMetrics( mfs ) ;

        return mfs ;
    }

    /**
     * 收集线程池信息
     * @param mfs
     */
    private void collectHystrixCommandMetrics(List<MetricFamilySamples> mfs){
        Collection<HystrixCommandMetrics> hystrixCommandMetrics = HystrixCommandMetrics.getInstances() ;
        if( hystrixCommandMetrics==null || hystrixCommandMetrics.size()<1 ){
            return;
        }

        // 标签属性
        List<String> labels = Arrays.asList("commandName" , "appName") ;

        // 并发请求数
        GaugeMetricFamily currentConcurrentExecutionSuccessCount = new GaugeMetricFamily("hystrix_concurrent_success",
                "hystrix每秒并发数", labels ) ;

        GaugeMetricFamily currentConcurrentExecutionFailCount = new GaugeMetricFamily("hystrix_concurrent_fail",
                "hystrix每秒失败数", labels ) ;

        GaugeMetricFamily hystrixCircuitBreakerGauge = new GaugeMetricFamily("hystrix_circuit_breaker_open",
                "hystrix是否熔断", labels ) ;

        for (HystrixCommandMetrics hystrixCommandMetric : hystrixCommandMetrics) {
            // 标签值
            String commandNameValue = commandNameConverter.convert( hystrixCommandMetric.getCommandKey().name() ) ;

            List<String> labelValues = Arrays.asList( commandNameValue , AppMonitorProperties.getAppName() ) ;

            // hystrix执行统计的时间窗口，单位秒
            int interval = hystrixCommandMetric.getProperties().metricsRollingStatisticalWindowInMilliseconds().get()/1000 ;
            // 并发量，按照时间窗口内总的请求数量 / 时间窗口长度
            currentConcurrentExecutionSuccessCount.addMetric( labelValues , hystrixCommandMetric.getRollingCount( HystrixEventType.SUCCESS ) / interval ) ;

            // 失败量，按照时间窗口内总的失败请求数量 / 时间窗口长度
            currentConcurrentExecutionFailCount.addMetric( labelValues , hystrixCommandMetric.getRollingCount( HystrixEventType.FAILURE ) / interval ) ;

//          瞬间绝对并发
//            currentConcurrentExecutionCount.addMetric( labelValues , hystrixCommandMetric.getCurrentConcurrentExecutionCount() ) ;

            HystrixCircuitBreaker hystrixCircuitBreaker = HystrixCircuitBreaker.Factory.getInstance( hystrixCommandMetric.getCommandKey() ) ;

            hystrixCircuitBreakerGauge.addMetric( labelValues , hystrixCircuitBreaker!=null && hystrixCircuitBreaker.isOpen() ? 1 : 0 ) ;
        }

        mfs.addAll( Lists.newArrayList( currentConcurrentExecutionSuccessCount ,
                currentConcurrentExecutionFailCount ,
                hystrixCircuitBreakerGauge ) ) ;

    }

    /**
     * 收集线程池信息
     * @param mfs
     */
    private void collectHystrixThreadPoolMetrics(List<MetricFamilySamples> mfs){
        Collection<HystrixThreadPoolMetrics> hystrixThreadPoolMetrics = HystrixThreadPoolMetrics.getInstances() ;
        if( hystrixThreadPoolMetrics==null || hystrixThreadPoolMetrics.size()<1 ){
            return ;
        }

        // 标签属性
        List<String> labels = Arrays.asList("groupName") ;
        // 并发线程数
        GaugeMetricFamily poolSize = new GaugeMetricFamily("hystrix_thread_pool",
                "运行核心线程数", labels ) ;

        // 并发线程数
        GaugeMetricFamily currentActiveCount = new GaugeMetricFamily("hystrix_thread_active",
                "并发线程数", labels ) ;

        GaugeMetricFamily currentLargestPoolSize = new GaugeMetricFamily("hystrix_thread_largest",
                "最大并发线程数", labels ) ;

        GaugeMetricFamily currentQueueSize = new GaugeMetricFamily("hystrix_thread_queue",
                "等待队列大小", labels ) ;

        for (HystrixThreadPoolMetrics hystrixThreadPoolMetric : hystrixThreadPoolMetrics) {
            // 标签值
            String groupName = groupNameConverter.convert( hystrixThreadPoolMetric.getThreadPoolKey().name() ) ;
            List<String> labelValues = Arrays.asList( groupName ) ;
            // 并发执行的线程数量
            currentActiveCount.addMetric( labelValues ,
                    hystrixThreadPoolMetric.getCurrentActiveCount().doubleValue()) ;

            poolSize.addMetric( labelValues ,
                    hystrixThreadPoolMetric.getCurrentPoolSize().doubleValue()) ;

            currentLargestPoolSize.addMetric( labelValues , hystrixThreadPoolMetric.getCurrentLargestPoolSize().doubleValue() ) ;

            currentQueueSize.addMetric( labelValues , hystrixThreadPoolMetric.getCurrentQueueSize().doubleValue() ) ;
        }

        mfs.addAll( Lists.newArrayList( currentActiveCount , currentLargestPoolSize , currentQueueSize ,poolSize) ) ;
    }
}
